/*! \file
 * \brief Enter a brief description here.
 *
 * Enter a detailed description here. (optional)
 * \author Author (Ivo Wingelaar)
 * \date Date (2011)
 * \version Version (0.0.1)
 * \copyright Copyright (GNU LGPL v3)
 */

#ifndef __LYNX_VERTEX_HPP_INCLUDED__
#define __LYNX_VERTEX_HPP_INCLUDED__

#include "LynxColor.hpp"

namespace Lynx
{

//! Enumeration for all vertex types there are.
enum E_VERTEX_TYPE
{
    //! Standard vertex type used by the Irrlicht engine, video::S3DVertex.
    EVT_STANDARD = 0,
    //! Vertex with two texture coordinates, video::S3DVertex2TCoords.
    EVT_2TCOORDS,
    //! Vertex with a tangent and binormal vector, video::S3DVertexTangents.
    EVT_TANGENTS
};

//! Standard vector.
struct Vertex
{
    Vertex()
    {
        //
    }

    Vertex(float x,
           float y,
           float z,
           float nx,
           float ny,
           float nz,
           Color c,
           float tu,
           float tv) :
        mPos(x, y, z),
        mNormal(nx, ny, nz),
        mColor(c),
        mTCoords(tu, tv)
    {
        //
    }

    Vertex(const Myth::Math::Vector3f pos,
           const Myth::Math::Vector3f normal,
           Color color,
           const Myth::Math::Vector2f tcoords) :
        mPos(pos),
        mNormal(normal),
        mColor(color),
        mTCoords(tcoords)
    {
        //
    }

    bool operator==(const Vertex& other) const
    {
        return ((mPos == other.mPos) && (mNormal == other.mNormal) && (mColor == other.mColor) && (mTCoords == other.mTCoords));
    }

    bool operator!=(const Vertex& other) const
    {
        return ((mPos != other.mPos) || (mNormal != other.mNormal) || (mColor != other.mColor) || (mTCoords != other.mTCoords));
    }

    bool operator<(const Vertex& other) const
    {
        return ((mPos < other.mPos) ||
                ((mPos == other.mPos) && (mNormal < other.mNormal)) ||
                ((mPos == other.mPos) && (mNormal == other.mNormal) && (mColor < other.mColor)) ||
                ((mPos == other.mPos) && (mNormal == other.mNormal) && (mColor == other.mColor) && (mTCoords < other.mTCoords)));
    }

    E_VERTEX_TYPE get_type() const
    {
        return EVT_STANDARD;
    }

    Myth::Math::Vector3f mPos;     ///< Position vector.
    Myth::Math::Vector3f mNormal;  ///< Normal vector.
    Color mColor;                  ///< Color.
    Myth::Math::Vector2f mTCoords; ///< Texture coordinates.
};


//! Vertex with two texture coordinates.
/** Usually used for geometry with lightmaps
or other special materials.
*/
struct Vertex2TCoords : public Vertex
{
    //! default constructor
    Vertex2TCoords() :
        Vertex()
    {
        //
    }

    Vertex2TCoords(Vertex o) :
        Vertex(o)
    {
        //
    }

    Vertex2TCoords(float x,
                   float y,
                   float z,
                   Color c,
                   float tu,
                   float tv,
                   float tu2,
                   float tv2) :
        Vertex(x, y, z, 0.0f, 0.0f, 0.0f, c, tu, tv),
        mTCoords2(tu2, tv2)
    {
        //
    }

    Vertex2TCoords(const Myth::Math::Vector3f pos,
                   Color color,
                   const Myth::Math::Vector2f tcoords,
                   const Myth::Math::Vector2f tcoords2) :
        Vertex(pos, Myth::Math::Vector3f(), color, tcoords),
        mTCoords2(tcoords2)
    {
        //
    }

    Vertex2TCoords(const Myth::Math::Vector3f pos,
                   const Myth::Math::Vector3f normal,
                   const Color color,
                   const Myth::Math::Vector2f tcoords,
                   const Myth::Math::Vector2f tcoords2) :
        Vertex(pos, normal, color, tcoords),
        mTCoords2(tcoords2)
    {
        //
    }

    Vertex2TCoords(float x,
                   float y,
                   float z,
                   float nx,
                   float ny,
                   float nz,
                   Color c,
                   float tu,
                   float tv,
                   float tu2,
                   float tv2) :
        Vertex(x, y, z, nx, ny, nz, c, tu, tv),
        mTCoords2(tu2,tv2)
    {
        //
    }

    Vertex2TCoords(float x,
                   float y,
                   float z,
                   float nx,
                   float ny,
                   float nz,
                   Color c,
                   float tu,
                   float tv) :
        Vertex(x, y, z, nx, ny, nz, c, tu, tv),
        mTCoords2(tu, tv)
    {
        //
    }

    Vertex2TCoords(const Myth::Math::Vector3f pos,
                   const Myth::Math::Vector3f normal,
                   Color color,
                   const Myth::Math::Vector2f tcoords) :
        Vertex(pos, normal, color, tcoords),
        mTCoords2(tcoords)
    {
        //
    }

    //! Equality operator
    bool operator==(const Vertex2TCoords& other) const
    {
        return ((static_cast<Vertex>(*this)==other) && (mTCoords2 == other.mTCoords2));
    }

    //! Inequality operator
    bool operator!=(const Vertex2TCoords& other) const
    {
        return ((static_cast<Vertex>(*this)!=other) || (mTCoords2 != other.mTCoords2));
    }

    bool operator<(const Vertex2TCoords& other) const
    {
        return ((static_cast<Vertex>(*this) < other) || ((static_cast<Vertex>(*this) == other) && (mTCoords2 < other.mTCoords2)));
    }

    E_VERTEX_TYPE get_type() const
    {
        return EVT_2TCOORDS;
    }

    Myth::Math::Vector2f mTCoords2; ///< Second set of texture coordinates.
};


//! Vertex with a tangent and binormal vector.
/** Usually used for tangent space normal mapping. */
struct VertexTangents : public Vertex
{
    //! default constructor
    VertexTangents() :
        Vertex()
    {
        //
    }

    //! constructor
    VertexTangents(float x,
                   float y,
                   float z,
                   float nx=0.0f,
                   float ny=0.0f,
                   float nz=0.0f,
                   Color c = 0xFFFFFFFF,
                   float tu=0.0f,
                   float tv=0.0f,
                   float tanx=0.0f,
                   float tany=0.0f,
                   float tanz=0.0f,
                   float bx=0.0f,
                   float by=0.0f,
                   float bz=0.0f) :
        Vertex(x, y, z, nx, ny, nz, c, tu, tv),
        mTangent(tanx, tany, tanz),
        mBinormal(bx, by, bz)
    {
        //
    }

    //! constructor
    VertexTangents(const Myth::Math::Vector3f pos,
                   Color c,
                   const Myth::Math::Vector2f tcoords) :
        Vertex(pos, Myth::Math::Vector3f(), c, tcoords)
    {
        //
    }

    //! constructor
    VertexTangents(const Myth::Math::Vector3f pos, const Myth::Math::Vector3f normal, Color c, const Myth::Math::Vector2f tcoords, const Myth::Math::Vector3f tangent = Myth::Math::Vector3f(), const Myth::Math::Vector3f binormal = Myth::Math::Vector3f()) :
        Vertex(pos, normal, c, tcoords),
        mTangent(tangent),
        mBinormal(binormal)
    {
        //
    }

    bool operator==(const VertexTangents& other) const
    {
        return ((static_cast<Vertex>(*this)==other) && (mTangent == other.mTangent) && (mBinormal == other.mBinormal));
    }

    bool operator!=(const VertexTangents& other) const
    {
        return ((static_cast<Vertex>(*this)!=other) || (mTangent != other.mTangent) || (mBinormal != other.mBinormal));
    }

    bool operator<(const VertexTangents& other) const
    {
        return ((static_cast<Vertex>(*this) < other) ||
                ((static_cast<Vertex>(*this) == other) && (mTangent < other.mTangent)) ||
                ((static_cast<Vertex>(*this) == other) && (mTangent == other.mTangent) && (mBinormal < other.mBinormal)));
    }

    E_VERTEX_TYPE get_type() const
    {
        return EVT_TANGENTS;
    }

    Myth::Math::Vector3f mTangent;  ///< Tangent vector along the x-axis of the texture
    Myth::Math::Vector3f mBinormal; ///< Binormal vector (tangent x normal)
};


inline uint32_t get_vertex_pitch_from_type(E_VERTEX_TYPE vertexType)
{
    switch(vertexType)
    {
    case EVT_2TCOORDS:
        return sizeof(Vertex2TCoords);
    case EVT_TANGENTS:
        return sizeof(VertexTangents);
    default:
        return sizeof(Vertex);
    }
}

} // namespace Lynx

#endif // __LYNX_VERTEX_HPP_INCLUDED__

